#include "GRO_Spinner.h"
#include "GRO_Gui.h"



// Every child of Widget should use these in draw()
#define DRAW_PAINTER(painter)     painter.draw(area, _gui->getRenderer());
#define DRAW_BOX     painters.box.draw(area, _gui->getRenderer());
#define DRAW_FRAME   painters.frame.draw(area, _gui->getRenderer());
#define DRAW_DESIGN  painters.design.drawDesign(area, designPush, _gui->getRenderer());


namespace GRO
{
    
    
    
    
    

    Spinner::Spinner()
            : Widget(SPINNER, Dimensions(20, 40))
            , linkedValue(NULL)
            , min(0)
            , value(50)
            , max(100)
            , increment(1)
            , plus(Dimensions(20, 20))
            , minus(Dimensions(20, 20))
            , clicked(NULL)
            , direction(UP)
            , repeatDelay(100)
            , repeatStart(0)
            , repeatInitDelay(500)
            , repeatLast(0)
            , repeating(0)
    {
        minus.onClick.connect<Spinner, void>(this, &Spinner::decrease)->priority = 3;
        plus.onClick.connect<Spinner, void>(this, &Spinner::increase)->priority = 3;
        
        minus.painters.design = getPainterMap()->Default_DownArrowDesignPainter;
        plus.painters.design = getPainterMap()->Default_UpArrowDesignPainter;
        
        //minus.setDesign(&Default_SpinDownArrowDesignPainter);
        minus.designPush = new Rect(4, 4, 4, 4);
        //plus.setDesign(&Default_SpinUpArrowDesignPainter);
        plus.designPush = new Rect(4, 4, 4, 4);
    }
    Spinner::Spinner(const Dimensions& dims)
            : Widget(SPINNER, dims)
            , linkedValue(NULL)
            , min(0)
            , value(50)
            , max(100)
            , increment(1)
            , plus(Dimensions(dims.w, dims.h/2))
            , minus(Dimensions(dims.w, dims.h/2))
            , clicked(NULL)
            , direction(UP)
            , repeatDelay(100)
            , repeatStart(0)
            , repeatInitDelay(500)
            , repeatLast(0)
            , repeating(0)
    {
        minus.onClick.connect<Spinner, void>(this, &Spinner::decrease)->priority = 3;
        plus.onClick.connect<Spinner, void>(this, &Spinner::increase)->priority = 3;
        
        
        minus.painters.design = getPainterMap()->Default_DownArrowDesignPainter;
        plus.painters.design = getPainterMap()->Default_UpArrowDesignPainter;
        
        //minus.setDesign(&Default_SpinDownArrowDesignPainter);
        minus.designPush = new Rect(4, 4, 4, 4);
        //plus.setDesign(&Default_SpinUpArrowDesignPainter);
        plus.designPush = new Rect(4, 4, 4, 4);
    }
    Spinner::Spinner(const Dimensions& dims, int initValue, int Min, int Max, const Dimensions& buttonDims, DirectionEnum Direction)
            : Widget(SPINNER, dims)
            , linkedValue(NULL)
            , min(Min)
            , value(initValue)
            , max(Max)
            , increment(1)
            , plus(buttonDims)
            , minus(buttonDims)
            , clicked(NULL)
            , direction(Direction)
            , repeatDelay(100)
            , repeatStart(0)
            , repeatInitDelay(500)
            , repeatLast(0)
            , repeating(0)
    {
        minus.onClick.connect<Spinner, void>(this, &Spinner::decrease)->priority = 3;
        plus.onClick.connect<Spinner, void>(this, &Spinner::increase)->priority = 3;
        
        minus.painters.design = getPainterMap()->Default_DownArrowDesignPainter;
        plus.painters.design = getPainterMap()->Default_UpArrowDesignPainter;
        /*if(direction == UP || direction == DOWN)
        {
            minus.setDesign(&Default_SpinDownArrowDesignPainter);
            plus.setDesign(&Default_SpinUpArrowDesignPainter);
        }
        else
        {
            minus.setDesign(&Default_SpinRightArrowDesignPainter);
            plus.setDesign(&Default_SpinLeftArrowDesignPainter);
        }*/
        minus.designPush = new Rect(4, 4, 4, 4);
        plus.designPush = new Rect(4, 4, 4, 4);
    }
    
    
    void Spinner::increase()
    {
        value += increment;
        if(value > max)
            value = max;
    }
    
    void Spinner::decrease()
    {
        value -= increment;
        if(value < min)
            value = min;
    }
    
    void Spinner::refreshTextOnClick(Text* text)
    {
        if(text != NULL)
        {
            plus.onClick.connect<Text, void>(text, &Text::refresh)->priority = 2;
            minus.onClick.connect<Text, void>(text, &Text::refresh)->priority = 2;
        }
    }
    
    /*void Spinner::applyTheme(Theme* theme, bool recursive = 0)
    {
        if(theme != NULL)
        {
            for(list<ThemeItem>::iterator e = theme->items.begin(); e != theme->items.end(); ++e)
            {
                if(e->widgetType == _type)
                {
                    bool first = e->level & THEMEITEM_FIRST, second = e->level & THEMEITEM_SECOND
                    , third = e->level & THEMEITEM_THIRD, fourth = e->level & THEMEITEM_FOURTH
                    , sub1 = e->level & THEMEITEM_SUB1, sub2 = e->level & THEMEITEM_SUB2
                    , sub3 = e->level & THEMEITEM_SUB3, sub4 = e->level & THEMEITEM_SUB4;
                    if(first && !(second || third || fourth) && !(sub1 || sub2 || sub3 || sub4))
                    {
                        if(e->box != NULL)
                            setBox(e->box);
                        if(e->frame != NULL)
                            setFrame(e->frame);
                        if(e->design != NULL)
                            setDesign(e->design);
                    }
                    else if(sub1)
                    {
                        // FIXME...  Make less leaky.
                        // Minus button
                        if(first && !(second || third || fourth))
                        {
                            if(e->box != NULL)
                                minus.setBox(e->box);
                            if(e->frame != NULL)
                                minus.setFrame(e->frame);
                            if(e->design != NULL)
                                minus.setDesign(e->design);
                        }
                        else if(second)
                        {
                            //if(e->box != NULL)
                                //minus.setBoxDown(e->box);
                            if(e->frame != NULL)
                                minus.setFrameDown(e->frame);
                            //if(e->design != NULL)
                                //minus.setDesignDown(e->design);
                        }
                    }
                    else if(sub2)
                    {
                        // FIXME...  Make less leaky.
                        // Plus button
                        if(first && !(second || third || fourth))
                        {
                            if(e->box != NULL)
                                plus.setBox(e->box);
                            if(e->frame != NULL)
                                plus.setFrame(e->frame);
                            if(e->design != NULL)
                                plus.setDesign(e->design);
                        }
                        else if(second)
                        {
                            //if(e->box != NULL)
                                //plus.setBoxDown(e->box);
                            if(e->frame != NULL)
                                plus.setFrameDown(e->frame);
                            //if(e->design != NULL)
                                //plus.setDesignDown(e->design);
                        }
                    }
                }
            }
        }
    }*/
    

    void Spinner::setGui(Gui* Gui)
    {
        if(Gui != NULL)
            Gui->registerWidget(this);
        
        _gui = Gui;
        plus.setGui(Gui);
        minus.setGui(Gui);
    }

    void Spinner::setGroup(Group* Group)
    {
        _group = Group;
        plus.setGroup(Group);
        minus.setGroup(Group);
    }

    void Spinner::setPanel(Panel* Panel)
    {
        _panel = Panel;
        plus.setPanel(Panel);
        minus.setPanel(Panel);
    }

    void Spinner::link(int* Value)
    {
        if (Value != NULL)
            linkedValue = Value;
    }

    void Spinner::draw(SDL_Rect* clipRect)
    {
        if(_folded || _hidden || _gui == NULL || _gui->getRenderer() == NULL)
            return;

        //Renderer* currentRenderer = _gui->getRenderer();

        SDL_Rect area = MakeRect(pos.x, pos.y, dims.w, dims.h);

        minus.draw(clipRect);
        plus.draw(clipRect);
        
        DRAW_FRAME;
    }

    void Spinner::setValue(int Value)
    {
        if (Value < min)
            Value = min;
        else if (Value > max)
            Value = max;
        if (linkedValue == NULL)
            value = Value;
        else
            *linkedValue = Value;
    }

    int Spinner::getValue()
    {
        if (linkedValue == NULL)
            return value;
        else
            return *linkedValue;
    }

    // Has actually been clicked
    Widget* Spinner::findWidget(int mx, int my)
    {
        if(_folded || _hidden || !InRect(mx, my, pos, dims))
            return NULL;
        
        /*if(InRect(mx, my, plus.pos, plus.dims))
        {
            return &plus;
        }
        else if(InRect(mx, my, minus.pos, minus.dims))
        {
            return &minus;
        }*/
        return this;
    }
    Widget* Spinner::action(PointerCursor* mouse, ActionEnum actionType)
    {
        if(_folded || _hidden || mouse == NULL)
            return NULL;
        if (InRect(mouse->mx, mouse->my, pos, dims))
        {
            if(actionType == ACTION_PRESS)
            {
                if(InRect(mouse->mx, mouse->my, plus.pos, plus.dims))
                {
                    clicked = &plus;
                    plus.action(mouse, actionType);
                }
                else if(InRect(mouse->mx, mouse->my, minus.pos, minus.dims))
                {
                    clicked = &minus;
                    minus.action(mouse, actionType);
                }
                
                repeatStart = SDL_GetTicks();
                repeatLast = repeatStart + repeatInitDelay;  // Get it ready for the first repeat
                
                if(clicked != NULL)
                    return clicked;
            }
            else if(actionType == ACTION_ACTIVATE)
            {
                if(clicked != NULL && !repeating)
                {
                    clicked->action(mouse, actionType);
                }
                minus.setPressed(0);
                plus.setPressed(0);
                repeating = 0;
                
                if(clicked != NULL)
                    return clicked;
            }
            else if(actionType == ACTION_HOVER)
            {
                // I should instead use a pointer to the cursor that is using me.
                // Use ACTION_LEAVE to stop repeating?
                Button* b = (minus.isPressed()? &minus : (plus.isPressed()? &plus : NULL));
                if(b != NULL)
                {
                    Uint32 ticks = SDL_GetTicks();
                    if(ticks - repeatStart > repeatInitDelay)
                    {
                        if(ticks - repeatLast > repeatDelay)
                        {
                            repeatLast = ticks;
                            if(InRect(mouse->mx, mouse->my, b->pos, b->dims))
                                b->onClick.emit();
                            repeating = 1;
                        }
                    }
                }
            }
            
            return this;
        }
        if(actionType == ACTION_ACTIVATE)
            if(clicked != NULL)
            {
                clicked->action(mouse, actionType);
                return clicked;
            }
        return NULL;
    }
    
    Widget* Spinner::action(NavCursor* nav, ActionEnum actionType)
    {
        if(_folded || _hidden || nav == NULL)
            return NULL;
        if(actionType == ACTION_PRESS)
        {
            // Enable scrolling.  All value changes (and repeating) are done through scrolling.
        }
        return this;
    }


    void Spinner::update()
    {
        switch (direction)
        {
        case UP:
        case DOWN:
            plus.pos = pos;
            minus.pos.x = pos.x;
            minus.pos.y = pos.y + plus.dims.h;
            break;
        case RIGHT:
        case LEFT:
            plus.pos = pos;
            minus.pos.x = pos.x + plus.dims.w;
            minus.pos.y = pos.y;
            break;
        }
        if(value < min)
            value = min;
        else if(value > max)
            value = max;
    }
    
    
    
    
    
    

TiXmlElement* Spinner::save(TiXmlElement* parent)
{
    TiXmlElement* w = new TiXmlElement("Spinner");
    parent->LinkEndChild(w);
    
    saveWidgetAttributes(w);
    
    w->SetAttribute("value", (linkedValue == NULL? value : *linkedValue));
    w->SetAttribute("min", min);
    w->SetAttribute("max", max);
    w->SetAttribute("increment", increment);
    w->SetAttribute("direction", direction);
    w->SetAttribute("repeatDelay", repeatDelay);
    w->SetAttribute("repeatInitDelay", repeatInitDelay);
    
    plus.save(w);
    minus.save(w);
    
    /*
    int* linkedValue;
    int min;
    int value;
    int max;

    int increment;

    Button plus;
    Button minus;
    Button* clicked;  // keep my own clicking state
    
    DirectionEnum direction;
    
    Uint32 repeatDelay;
    Uint32 repeatStart;
    Uint32 repeatInitDelay;
    Uint32 repeatLast;
    bool repeating;*/
    
    return w;
}

int getInt(const std::string& s);
Beacon* getBeaconByName(Widget* w, const std::string& name);
    
void Spinner::connectBeacon(Widget* widget, Beacon* beacon, const std::string& fn_name, const std::vector<std::string>& args)
{
    Widget* w = widget;
    if(w == NULL)
        return;
    
    Beacon* b = beacon;
    if(b == NULL)
        return;
    
    if(fn_name == "increase" && args.size() == 0)
    {
        b->connect<Spinner, void>(this, &Spinner::increase);
    }
    else if(fn_name == "decrease" && args.size() == 0)
    {
        b->connect<Spinner, void>(this, &Spinner::decrease);
    }
    else if(fn_name == "setValue" && args.size() == 1)
    {
        b->connect<Spinner, void, int>(this, &Spinner::setValue, getInt(args[0]));
    }
    else
        Widget::connectBeacon(widget, beacon, fn_name, args);
    return;
}
    
    
    
    
    
    
}
